package org.wisdom.security.config;

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter;

/**
 * 认证配置类
 * 
 * @author zhangzhiyan
 * @date 2021/08/19 16:01
 **/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@AutoConfigureBefore({SecurityAutoConfiguration.class})
@EnableConfigurationProperties({JwtSecurityProperties.class})
@RequiredArgsConstructor
public class JwtSecurityAutoConfiguration {

    @Value("${wisdom.ac.name:wisdom-ac}")
    private String ac;

    /**
     * 添加权限过滤 spring security 新的扩展点，由于新扩展点与旧的继承WebSecurityConfigurerAdapter扩展不能同时存在 因此调整旧扩展点加载顺序优先于默认的新扩展权限过滤器，避免冲突
     *
     * @author 张智研
     * @date 2022/06/02 17:54
     * @param httpSecurity
     *            httpSecurity
     * @param authenticationConfiguration
     *            authenticationConfiguration
     * @return org.springframework.security.web.SecurityFilterChain
     */
    @Bean("wisdom-starter-security#JwtSecurityAutoConfiguration")
    @Order(-100)
    SecurityFilterChain filterChain(HttpSecurity httpSecurity, AuthenticationConfiguration authenticationConfiguration)
        throws Exception {
        httpSecurity
            // 禁用 CSRF
            .csrf().disable()
            // 授权异常
            .exceptionHandling().authenticationEntryPoint(new Http401UnauthorizedEntryPoint())
            .accessDeniedHandler(new JwtAccessDeniedHandler())
            // 防止iframe 造成跨域
            .and().headers().frameOptions().disable()
            // 不创建会话
            .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and().authorizeRequests()
            // 所有options请求，放行
            .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
            // 放行/auth/**请求
            .mvcMatchers("/" + this.ac + "/auth/**").permitAll()
            .antMatchers("/druid/**").permitAll()
            .anyRequest().authenticated()
            // 添加HeaderAuthentication过滤器，过滤token令牌
            .and().addFilterAfter(requestHeaderAuthenticationFilter(authenticationConfiguration),
                UsernamePasswordAuthenticationFilter.class)
            // 禁用缓存
            .headers().cacheControl();

        return httpSecurity.build();
    }

    /**
     * 自定义过滤器
     * 
     * @author 张智研
     * @date 2022/06/02 17:54
     * @return org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer
     */
    @Bean
    public WebSecurityCustomizer ignoringCustomizer() {
        // 忽略 /error 页面
        return web -> web.ignoring().antMatchers("/error");
    }

    /**
     * Authorization证书过滤器
     *
     * @author 张智研
     * @date 2022/05/17 15:17
     * @return org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter
     */
    private RequestHeaderAuthenticationFilter
        requestHeaderAuthenticationFilter(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
        filter.setPrincipalRequestHeader(HttpHeaders.AUTHORIZATION);
        filter.setAuthenticationManager(authenticationConfiguration.getAuthenticationManager());
        filter.setExceptionIfHeaderMissing(false);
        return filter;
    }
}
